from __future__ import division

import math

from concepts import *

from .good import Good
from .storage import Storage
from .process import CallbackProcess


class Person(Object):
    color     =_ (None)
    speed     =_ (10)
    roadbound =_ (False)
    capacity  =_ (None)
    is_ship   =_ (False)
    storage   =_ (None)
    
    city          = Attribute()
    position      = Attribute(writable = True)
    real_position = Attribute(writable = True)
    calendar      = city._.calendar
    x             = position._.x
    y             = position._.y
    real_x        = real_position._.x
    real_y        = real_position._.y
    
    home = Attribute(writable = True)
    
    storage      = Attribute()
    create       = storage._.add_goods
    consume      = storage._.remove_goods
    take_from    = storage._.transfer_from
    give_to      = storage._.transfer_to
    can_hold     = storage._.can_store
    can_hold_all = storage._.can_store_all
    load_from    = storage._.load_from
    unload_into  = storage._.unload_into
    
    step  = Attribute(writable = True)
    dying = Attribute(initial = False, writable = True)
    
    processes = ListAttribute()
    
    def setup(self):
        if self.capacity is not None:
            self._storage = Storage((Good(self.capacity),))
        
        self.birth()
        self.real_position = self.position
    
    def birth(self):
        pass
    
    def can_pass(self, tile):
        return ((self.is_ship and tile.is_water) or
                (self.roadbound and tile.is_road) or
                (not self.is_ship and not self.roadbound and tile.is_passable))
    
    def on_day(self, day, month, year):
        for process in self.processes:
            process.advance()
        
        self.day(day, month, year)
    
    def day(self, day, month, year):
        pass
    
    def on_tick(self):
        if self.dying:
            self.die()
        elif self.home is not None and not self.home.exists:
            self.on_homeless()
        else:
            self.do_movement(1)
    
    def on_homeless(self):
        self.destroy()
    
    def do_movement(self, fraction):
        if self.step is None:
            self.move_towards(self.position, fraction)
        else:
            self.move_towards(self.step, fraction)
    
    def move_towards(self, position, fraction = 1):
        if self.real_position == position:
            self.waiting()
            
            return
        
        distance_moved  = self.speed / self.calendar.ticks_per_day * fraction
        distance_needed = self.real_position.distance(position)
        
        if distance_needed <= distance_moved:
            self.real_position = position
            self.arriving_at(position)
            
            fraction = (distance_moved - distance_needed) / distance_needed
            
            if fraction > 0.001:
                self.do_movement(fraction)
        else:
            direction = self.real_position.direction_of(position).with_length(distance_moved)
            
            self.real_position = self.real_position.in_direction(direction)
            self.moving()
           
        if self.real_position.rounded != self.position:
            self.position = self.real_position.rounded
    
    def process(self, callback, speed):
        self._processes.append(CallbackProcess(speed * self.calendar.ticks_per_day, callback))
    
    @property
    def tile(self):
        return self._city._map.at(self._position)
    
    @property
    def exists(self):
        return self.position is not None and not self.dying
    
    def is_at(self, position):
        return self.position == position
    
    def path_to(self, target):
        return self.tile.path_to(target, self.roadbound, self.is_ship)
    
    def moving(self):
        pass
    
    def waiting(self):
        pass
    
    def arriving_at(self, position):
        pass
    
    def destroy(self, update_city = True):
        if not update_city:
            return
        
        self.dying = True
    
    def die(self):
        self.city.remove(self)
        self.position = None
        self.dying    = False
    
    def __repr__(self):
        return '<%s @ %s>' % (self.__class__.__name__, self.position)
